﻿namespace LightCAD.Core
{
    public struct Vector3d
    {
        public double X;
        public double Y;
        public double Z;

        public Vector3d(double x = 0.0, double y = 0.0, double z = 0.0)
        {
            this.X = x;
            this.Y = y;
            this.Z = z;
        }

        public void Set(double newX, double newY, double newZ)
        {
            this.X = newX;
            this.Y = newY;
            this.Z = newZ;
        }

        public override string ToString()
        {
            return string.Format("Vector3({0}, {1}, {2})", this.X, this.Y, this.Z);
        }

        public override bool Equals(object obj)
        {
            if (!(obj is Vector3d))
                return false;

            return Equals((Vector3d)obj);
        }

        public bool Equals(Vector3d rhs)
        {
            return Utils.IsEqual(X, rhs.X)
                && Utils.IsEqual(Y, rhs.Y)
                && Utils.IsEqual(Z, rhs.Z);
        }

        public override int GetHashCode()
        {
            return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
        }

        public double Length
        {
            get
            {
                return Math.Sqrt(((this.X * this.X) + (this.Y * this.Y)) + (this.Z * this.Z));
            }
        }

        public double LengthSqrd
        {
            get
            {
                return (((this.X * this.X) + (this.Y * this.Y)) + (this.Z * this.Z));
            }
        }

        public void Normalize()
        {
            double length = this.Length;
            if (length != 0.0)
            {
                this.X /= length;
                this.Y /= length;
                this.Z /= length;
            }
        }

        public Vector3d Normalized
        {
            get
            {
                double length = this.Length;
                if (length != 0.0)
                {
                    return new Vector3d(this.X / length, this.Y / length, this.Z / length);
                }
                return this;
            }
        }







        //public static double Dot(Vector3d a, Vector3d b)
        //{
        //    return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
        //}

        //public static Vector3d Cross(Vector3d a, Vector3d b)
        //{
        //    return new Vector3d(
        //        (a.Y * b.Z) - (a.Z * b.Y),
        //        (a.Z * b.X) - (a.X * b.Z),
        //        (a.X * b.Y) - (a.Y * b.X));
        //}

        ///// <summary>
        ///// Returns the unsigned angle in degrees between a and b.
        ///// The smaller of the two possible angles between the two vectors is used.
        ///// The result value range: [0, 180]
        ///// </summary>
        //public static double Angle(Vector3d a, Vector3d b)
        //{
        //    return Utils.RadianToDegree(AngleInRadian(a, b));
        //}

        ///// <summary>
        ///// Returns the unsigned angle in radians between a and b.
        ///// The smaller of the two possible angles between the two vectors is used.
        ///// The result value range: [0, PI]
        ///// </summary>
        //public static double AngleInRadian(Vector3d a, Vector3d b)
        //{
        //    double num = a.Length * b.Length;
        //    if (num == 0.0)
        //    {
        //        return 0.0;
        //    }
        //    double num2 = Dot(a, b) / num;
        //    return Math.Acos(Utils.Clamp(num2, -1.0, 1.0));
        //}

        ///// <summary>
        ///// Returns the signed acute clockwise angle in degrees between from and to.
        ///// The result value range: [-180, 180]
        ///// <param name="from">The vector from which the angular difference is measured.</param>
        ///// <param name="to">The vector to which the angular difference is measured.</param>
        ///// <param name="axis">A vector around which the other vectors are rotated.</param>
        ///// </summary>
        //public static double SignedAngle(Vector3d from, Vector3d to, Vector3d axis)
        //{
        //    return Utils.RadianToDegree(SignedAngleInRadian(from, to, axis));
        //}

        ///// <summary>
        ///// Returns the signed acute clockwise angle in radians between from and to.
        ///// The result value range: [-PI, PI]
        ///// <param name="from">The vector from which the angular difference is measured.</param>
        ///// <param name="to">The vector to which the angular difference is measured.</param>
        ///// <param name="axis">A vector around which the other vectors are rotated.</param>
        ///// </summary>
        //public static double SignedAngleInRadian(Vector3d from, Vector3d to, Vector3d axis)
        //{
        //    double rad = AngleInRadian(from, to);
        //    Vector3d n = Cross(from, to);
        //    if (Dot(n, axis) < 0)
        //    {
        //        rad = -rad;
        //    }

        //    return rad;
        //}

        public static double Distance(Vector3d a, Vector3d b)
        {
            return (b - a).Length;
        }

        public static Vector3d operator +(Vector3d a, Vector3d b)
        {
            return new Vector3d(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
        }

        public static Vector3d operator -(Vector3d a, Vector3d b)
        {
            return new Vector3d(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
        }

        public static Vector3d operator -(Vector3d v)
        {
            return new Vector3d(-v.X, -v.Y, -v.Z);
        }

        public static Vector3d operator *(Vector3d v, double d)
        {
            return new Vector3d(v.X * d, v.Y * d, v.Z * d);
        }

        public static Vector3d operator *(double d, Vector3d v)
        {
            return new Vector3d(v.X * d, v.Y * d, v.Z * d);
        }

        public static Vector3d operator /(Vector3d v, double d)
        {
            return new Vector3d(v.X / d, v.Y / d, v.Z / d);
        }

        public static bool operator ==(Vector3d lhs, Vector3d rhs)
        {
            return lhs.Equals(rhs);
        }

        public static bool operator !=(Vector3d lhs, Vector3d rhs)
        {
            return !(lhs == rhs);
        }
    }
}
